Page({
    data: {
        //#region 纯数据字段
        /** 屏幕高度，单位px */
        _windowHeight: null,
        /** 开始触摸时的单一项左上角y坐标 */
        _startOffsetY: null,
        /** 开始触摸位置y坐标 */
        _startPageY: null,
        /** 开始触摸项索引 */
        _startDragElementIndex: null,

        /** 滑动偏移 */
        _scrollThreshold: 0.5,

        /** 距顶部/左边多远时，触发 _scrollToUpper 事件，单位px，即上滑至屏幕顶部 */
        _upperThreshold: 100,
        /** 距底部/右边多远时，触发 _scrollToLower 事件，单位px，即下滑至屏幕底部 */
        _lowerThreshold: 100,
        /** 上滑和下滑时间，单位毫秒 */
        _scrollDuration: 1000,
        //#endregion

        /** 列表 */
        list: [{
            id: 1,
            content: 1
        }, {
            id: 2,
            content: 2
        }, {
            id: 3,
            content: 3
        }, {
            id: 4,
            content: 4
        }, {
            id: 5,
            content: 5
        }, {
            id: 6,
            content: 6
        }, {
            id: 7,
            content: 7
        }, {
            id: 8,
            content: 8
        }, {
            id: 9,
            content: 9
        }, {
            id: 10,
            content: 10
        }, {
            id: 11,
            content: 11
        }, {
            id: 12,
            content: 12
        }],

        /** 单一项高度 */
        elementHeight: 80,

        /** 滑动项 */
        dragElement: null,
        /** movable-view组件y轴坐标，滑动时滑动项左上角距离文档顶部纵坐标，单位px */
        movableViewY: null,
        /** 滑动过程中经过的项 */
        lastTarget: null,
    },

    /** 生命周期函数--监听页面展示 */
    onShow() {
        this._getWindowHeight();
    },
    /** 长按触发事件 */
    onLongPress(event) {
        console.log('[onLongPress]', event);

        let dragElementIndex = event.currentTarget.dataset.index;
        let dragElement = this.data.list[dragElementIndex];
        this.setData({
            /** 点击项左上角y坐标 */
            _startOffsetY: event.target.offsetTop,
            /** 点击位置y坐标 */
            _startPageY: event.touches[0].pageY,
            /** 点击项索引 */
            _startDragElementIndex: dragElementIndex,
            /** 点击项 */
            dragElement,
            /** movable-view组件左上角y坐标 */
            movableViewY: event.target.offsetTop
        });
    },
    /**
     * 手指触摸后移动
     * - 触底或触顶时下滑或者上滑
     * - 移动movable-view
     */
    onTouchMove(event) {
        console.log('[onTouchMove]', event);

        // 长按事件
        if (this.data.dragElement) {
            /** 触摸点位置在显示屏幕区域左上角Y坐标 */
            let clientY = event.touches[0].clientY;        
            /** 触摸点位置距离文档左上角Y坐标 */
            let pageY = event.touches[0].pageY;
            /** 和最初点击位置比较移动距离 */
            let targetMoveDistance = pageY - this.data._startPageY;
            /** 移动后的movable-view组件位置 */
            let movableViewY = this.data._startOffsetY + targetMoveDistance;
            /** 经过项索引 */
            let targetIndex = this._computeFutureIndex(targetMoveDistance, this.data._startDragElementIndex);

            this._pageScroll(clientY, pageY);

            this.setData({
                movableViewY,
                lastTarget: targetIndex
            });
        }
    },
    /** 滑动结束 */
    onTouchEnd(event) {
        console.log('[onTouchEnd]', event);

        if (this.data.dragElement) {
            let list = this._deepCopy(this.data.list);
            /** 结束点位置y坐标 */
            let pageY = event.changedTouches[0].pageY;
            /** 和初始点击位置比较移动距离 */
            let targetMoveDistance = pageY - this.data._startPageY;
            /** 初始点击项索引 */
            let dragElementIndex = this.data._startDragElementIndex;

            /** 目标项索引 */
            const futureIndex = this._computeFutureIndex(targetMoveDistance, dragElementIndex);
            if (futureIndex !== false) {
                list.splice(futureIndex, 0, list.splice(dragElementIndex, 1)[0]);  // 移动位置
            }

            this.setData({
                list,
                dragElement: null,
                lastTarget: null
            });
        }
    },
    /** 阻止滑动 */
    onHackTouchMove() { },
    /** 获取可使用窗口高度，单位px */
    _getWindowHeight() {
        try {
            const { windowHeight } = wx.getSystemInfoSync();
            this.setData({
                _windowHeight: windowHeight
            });
        } catch (err) {
            console.error('[_getWindowHeight]', err);
        }
    },
    /** 页面滑动 */
    _pageScroll(clientY, pageY) {
        if (clientY + this.data._upperThreshold >= this.data._windowHeight) {
            // 下滑接近屏幕底部
            wx.pageScrollTo({
                scrollTop: pageY + this.data.elementHeight,
                duration: this.data._scrollDuration
            });
        } else if (clientY - this.data._lowerThreshold <= 0) {
            // 上滑接近屏幕顶部
            wx.pageScrollTo({
                scrollTop: pageY - this.data.elementHeight,
                duration: this.data._scrollDuration
            })
        }
    },
    /**
     * 计算目标索引
     * @param {number} targetMoveDistance 移动距离
     * @param {number} dtagElementIndex 初始移动项索引
     * 若轻轻拂动则返回false
     */
    _computeFutureIndex(targetMoveDistance, dragElementIndex) {
        let willInsertAfter = this._getSwapDirection(targetMoveDistance);
        if (willInsertAfter !== false) {            
            /** 偏移索引 */
            let offsetElementIndex = dragElementIndex + willInsertAfter;
            /** 移动步数 */
            let step = targetMoveDistance / this.data.elementHeight;
            /** 步数补偿，当只有移动距离超过单项 _scrollThreshold 时才算有效 */
            if (step <= -1) {
                step += this.data._scrollThreshold;
            } else if (step >= 1) {
                step -= this.data._scrollThreshold;
            }
            /** 目标索引 */
            let futureIndex = parseInt(step) + offsetElementIndex;            

            // 避免越界
            if (futureIndex < 0) {
                futureIndex = 0;
            } else if (futureIndex > this.data.list.length - 1) {
                futureIndex = this.data.list.length - 1;
            }

            return futureIndex;
        } else {
            return willInsertAfter;
        }
    },
    /**
     * 获取滑动方向
     * @param {number} targetMoveDistance 移动距离
     * @returns {number/boolean}
     *  - 1 下滑
     *  - -1 上滑
     *  - false 拂动，滑动距离小于一半单项高度
     */
    _getSwapDirection(targetMoveDistance) {
        if (Math.abs(targetMoveDistance) < this.data.elementHeight / 2) {
            // 轻轻拂动，滑动距离小于1/2单项高度
            return false;
        } else if (targetMoveDistance >= this.data.elementHeight / 2) {
            console.log('[_getSwapDirection] 👇👇👇');
            return 1;  // 下滑
        } else if (targetMoveDistance <= this.data.elementHeight / -2) {
            console.log('[_getSwapDirection] 👆👆👆');
            return -1;  // 上滑
        }
    },
    /** 深拷贝 */
    _deepCopy(obj) {
        // 只拷贝对象
        if (typeof obj !== 'object') return;
        // 根据obj的类型判断是新建一个数组还是一个对象
        var newObj = obj instanceof Array ? [] : {};
        for (var key in obj) {
            // 遍历obj,并且判断是obj的属性才拷贝
            if (obj.hasOwnProperty(key)) {
                // 判断属性值的类型，如果是对象递归调用深拷贝
                newObj[key] = typeof obj[key] === 'object' ? this._deepCopy(obj[key]) : obj[key];
            }
        }
        return newObj;
    }
})

